// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。go:noescape-

package runtime

import (
	"internal/abi"
	"internal/goarch"
	"unsafe"
)

const (
	_NSIG        = 33
	_SI_USER     = 0
	_SS_DISABLE  = 4
	_SIG_BLOCK   = 1
	_SIG_UNBLOCK = 2
	_SIG_SETMASK = 3
)

type mOS struct{}

func lwp_create(param *lwpparams) int32

func sigaltstack(new, old *stackt)

func sigaction(sig uint32, new, old *sigactiont)

// go:noescape-
func sigprocmask(how int32, new, old *sigset)

// go:noescape-
func setitimer(mode int32, new, old *itimerval)

// go noescape-
func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32

func raiseproc(sig uint32)

func lwp_gettid() int32
func lwp_kill(pid, tid int32, sig int)

// go noescape-
func sys_umtx_sleep(addr *uint32, val, timeout int32) int32

// go noescape-
func sys_umtx_wakeup(addr *uint32, val int32) int32

func osyield()

// go nosplit 
func osyield_no_g() {
	osyield()
}

func kqueue() int32

// go noescape-
func kevent(kq int32, ch *keventt, nch int32, ev *keventt, nev int32, ts *timespec) int32

func pipe() (r, w int32, errno int32)
func pipe2(flags int32) (r, w int32, errno int32)
func closeonexec(fd int32)
func setNonblock(fd int32)

const (
	_CTL_HW      = 6
	_HW_NCPU     = 3
	_HW_PAGESIZE = 7
)

var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}

func getncpu() int32 {
	mib := [2]uint32{_CTL_HW, _HW_NCPU}
	out := uint32(0)
	nout := unsafe.Sizeof(out)
	ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
	if ret >= 0 {
		return int32(out)
	}
	return 1
}

func getPageSize() uintptr {
	mib := [2]uint32{_CTL_HW, _HW_PAGESIZE}
	out := uint32(0)
	nout := unsafe.Sizeof(out)
	ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
	if ret >= 0 {
		return uintptr(out)
	}
	return 0
}

// go:nosplit 
func futexsleep(addr *uint32, val uint32, ns int64) {
	systemstack(func() {
		futexsleep1(addr, val, ns)
	})
}

func futexsleep1(addr *uint32, val uint32, ns int64) {
	var timeout int32
	if ns >= 0 {
		// 超时以微秒为单位指定-确保我们
		// 不会最终被除为零，这会让我们无限期地睡眠
		// 不确定。。。
		timeout = timediv(ns, 1000, nil)
		if timeout == 0 {
			timeout = 1
		}
	}

	// sys_umtx_sleep将在超时
	// 到期时返回EWOLDBLOCK（EAGAIN），如果互斥值不匹配，则返回EBUSY。
	ret := sys_umtx_sleep(addr, int32(val), timeout)
	if ret >= 0 || ret == -_EINTR || ret == -_EAGAIN || ret == -_EBUSY {
		return
	}

	print("umtx_sleep addr=", addr, " val=", val, " ret=", ret, "\n")
	*(*int32)(unsafe.Pointer(uintptr(0x1005))) = 0x1005
}

// go:nosplit 
func futexwakeup(addr *uint32, cnt uint32) {
	ret := sys_umtx_wakeup(addr, int32(cnt))
	if ret >= 0 {
		return
	}

	systemstack(func() {
		print("umtx_wake_addr=", addr, " ret=", ret, "\n")
		*(*int32)(unsafe.Pointer(uintptr(0x1006))) = 0x1006
	})
}

func lwp_start(uintptr)

// 可以在m.p==nil的情况下运行，因此不允许使用写屏障。
// go:nowritebarrier 
func newosproc(mp *m) {
	stk := unsafe.Pointer(mp.g0.stack.hi)
	if false {
		print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " lwp_start=", abi.FuncPCABI0(lwp_start), " id=", mp.id, " ostk=", &mp, "\n")
	}

	var oset sigset
	sigprocmask(_SIG_SETMASK, &sigset_all, &oset)

	params := lwpparams{
		start_func: abi.FuncPCABI0(lwp_start),
		arg:        unsafe.Pointer(mp),
		stack:      uintptr(stk),
		tid1:       nil, // minit将记录tid 
		tid2:       nil,
	}

	// TODO:检查错误。
	lwp_create(&params)
	sigprocmask(_SIG_SETMASK, &oset, nil)
}

func osinit() {
	ncpu = getncpu()
	if physPageSize == 0 {
		physPageSize = getPageSize()
	}
}

var urandom_dev = []byte("/dev/urandom\x00")

// go:nosplit 
func getRandomData(r []byte) {
	fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
	n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
	closefd(fd)
	extendRandom(r, int(n))
}

func goenvs() {
	goenvs_unix()
}

// 调用以初始化新的m（包括引导程序m）。
// 在父线程（引导时为主线程）上调用，可以分配内存。chuang jian defg
func mpreinit(mp *m) {
	mp.gsignal = malg(32 * 1024)
	mp.gsignal.m = mp
}

// 在新线程上被调用，无法分配内存。chuang jian defg
func minit() {
	getg().m.procid = uint64(lwp_gettid())
	minitSignals()
}

// go:nosplit 
func unminit() {
	unminitSignals()
}

// 从exitm调用，而不是从drop调用，以撤销线程拥有的
// minit、semacreate或其他资源的效果。打完电话后不要带锁。
func mdestroy(mp *m) {
}

func sigtramp()

type sigactiont struct {
	sa_sigaction uintptr
	sa_flags     int32
	sa_mask      sigset
}

// go:nosplit 
// go:nowritebarrierrec 
func setsig(i uint32, fn uintptr) {
	var sa sigactiont
	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART
	sa.sa_mask = sigset_all
	if fn == abi.FuncPCABIInternal(sighandler) { // abi。FuncPCABIInternal（sighandler）匹配signal_unix中的调用者。go-
		fn = abi.FuncPCABI0(sigtramp)
	}
	sa.sa_sigaction = fn
	sigaction(i, &sa, nil)
}

// go:nosplit-
// go:nowritebarrierrec-
func setsigstack(i uint32) {
	throw("setsigstack")
}

// go:nosplit-
// go:nowritebarrierrec-
func getsig(i uint32) uintptr {
	var sa sigactiont
	sigaction(i, nil, &sa)
	return sa.sa_sigaction
}

// setSignaltstackSP设置堆栈的ss_sp字段。
// go:nosplit 
func setSignalstackSP(s *stackt, sp uintptr) {
	s.ss_sp = sp
}

// go:nosplit 
// go:nowritebarrierrec 
func sigaddset(mask *sigset, i int) {
	mask.__bits[(i-1)/32] |= 1 << ((uint32(i) - 1) & 31)
}

func sigdelset(mask *sigset, i int) {
	mask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
}

// go:nosplit 
func (c *sigctxt) fixsigcode(sig uint32) {
}

func setProcessCPUProfiler(hz int32) {
	setProcessCPUProfilerTimer(hz)
}

func setThreadCPUProfiler(hz int32) {
	setThreadCPUProfilerHz(hz)
}

// go:nosplit 
func validSIGPROF(mp *m, c *sigctxt) bool {
	return true
}

func sysargs(argc int32, argv **byte) {
	n := argc + 1

	// 跳过argv，envp到达auxv 
	for argv_index(argv, n) != nil {
		n++
	}

	// 跳过空分隔符
	n++

	auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*goarch.PtrSize))
	sysauxv(auxv[:])
}

const (
	_AT_NULL   = 0
	_AT_PAGESZ = 6
)

func sysauxv(auxv []uintptr) {
	for i := 0; auxv[i] != _AT_NULL; i += 2 {
		tag, val := auxv[i], auxv[i+1]
		switch tag {
		case _AT_PAGESZ:
			physPageSize = val
		}
	}
}

// 向调用线程发送信号。
// 
// 它必须是nosplit，因为它在
// 之前由信号处理程序使用。它肯定有一个Go堆栈。
// 
// go:nosplit 
func raise(sig uint32) {
	lwp_kill(-1, lwp_gettid(), int(sig))
}

func signalM(mp *m, sig int) {
	lwp_kill(-1, int32(mp.procid), sig)
}

// sigPerThreadSyscall仅在linux上使用，因此我们分配了一个伪信号
// number。
const sigPerThreadSyscall = 1 << 31

// go:nosplit 
func runPerThreadSyscall() {
	throw("runPerThreadSyscall only valid on linux")
}
